﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Drawing.Drawing2D;

namespace d39
{
    public partial class GridImage : System.Web.UI.Page
    {
        protected void Page_Init(object sender, EventArgs e)
        {
            Response.ContentType = "image/png";
            Response.SetCachingHeaders();
            var grid = Grid.FromQueryString(Request.QueryString);

            var tweaks = new UrlTweaks() { IncludeLegendInImage = Request.QueryString["l"] != null };

            using (var image = new Bitmap(grid.GetImageWidth(tweaks), grid.GetImageHeight(tweaks), PixelFormat.Format32bppArgb))
            {
                using (var graphics = Graphics.FromImage(image))
                {
                    graphics.SmoothingMode = SmoothingMode.HighQuality;
                    graphics.CompositingMode = CompositingMode.SourceOver;
                    using (var brush = new SolidBrush(grid.BackgroundColor))
                    {
                        graphics.FillRectangle(brush, 0, 0, image.Width, image.Height);
                    }
                    if (tweaks.IncludeLegendInImage)
                    {
                        int maxSize = grid.Players.Max(p => p.SizeInSquares, 1);
                        var pos = new Coordinate(grid.Width + maxSize + 2, 2);
                        using (var font = new Font(FontFamily.GenericSansSerif, grid.PixelSize + 2, GraphicsUnit.Pixel))
                        {
                            using (var smallFont = new Font(FontFamily.GenericSansSerif, grid.PixelSize * 0.8f + 2, GraphicsUnit.Pixel))
                            {
                                var textOffset = new SizeF(5, -3);
                                var descrOffset = new SizeF(5, -1);
                                foreach (var player in grid.Players)
                                {
                                    player.Draw(graphics, grid, pos + Vector.W * player.SizeInSquares, !player.IsAllInvisible, player.IsAllInactive, Moves.None);
                                    using (var brush = new SolidBrush(player.Color))
                                    {
                                        graphics.DrawString(player.NameAndPositions, font, brush, grid.CoordinateToPoint(pos) + textOffset);
                                        if (!string.IsNullOrWhiteSpace(player.Description))
                                        {
                                            pos += Vector.S;
                                            graphics.DrawString(player.Description, smallFont, brush, grid.CoordinateToPoint(pos) + descrOffset);
                                        }
                                        foreach (var s in player.GetRowsOfPositions(grid))
                                        {
                                            pos += Vector.S;
                                            graphics.DrawString(s, smallFont, brush, grid.CoordinateToPoint(pos) + descrOffset);
                                        }
                                    }
                                    pos += Vector.S * (player.SizeInSquares + 1);
                                }
                            }
                        }
                        graphics.SetClip(new Rectangle(0, 0, grid.GetImageWidth(UrlTweaks.Default), grid.GetImageHeight(UrlTweaks.Default)));
                    }
                    foreach (var terrain in grid.TerrainTypes)
                    {
                        foreach (var seg in terrain.Segments)
                        {
                            using (var brush = new SolidBrush(terrain.ColorWithOpacity))
                            {
                                graphics.FillPolygon(brush, (from c in seg.Coordinates select grid.CoordinateToPoint(c)).ToArray());
                            }
                        }
                    }
                    using (var gridPen = new Pen(grid.GridLineColor))
                    {
                        for (int i = 0; i <= grid.Width; i++)
                        {
                            graphics.DrawLine(gridPen, grid.CoordinateToPoint(new Coordinate(i + 1, 1)), grid.CoordinateToPoint(new Coordinate(i + 1, grid.Height + 1)));
                        }
                        for (int i = 0; i <= grid.Height; i++)
                        {
                            graphics.DrawLine(gridPen, grid.CoordinateToPoint(new Coordinate(1, i + 1)), grid.CoordinateToPoint(new Coordinate(grid.Width + 1, i + 1)));
                        }
                    }
                    foreach (var terrain in grid.TerrainTypes.Where(t => t.PerimeterThickness > 0))
                    {
                        using (var pen = new Pen(terrain.PerimeterColorWithOpacity, terrain.PerimeterThickness))
                        {
                            pen.DashStyle = terrain.PerimeterDashStyle;
                            foreach (var seg in terrain.Segments)
                            {
                                graphics.DrawPolygon(pen, (from c in seg.Coordinates select grid.CoordinateToPoint(c)).ToArray());
                            }
                        }
                    }
                    foreach (var wall in grid.WallTypes)
                    {
                        using (var pen = new Pen(wall.ColorWithOpacity, wall.Thickness))
                        {
                            pen.DashStyle = wall.DashStyle;
                            foreach (var seg in wall.Segments)
                            {
                                if (seg.Coordinates.First() == seg.Coordinates.Last())
                                {
                                    graphics.DrawPolygon(pen, (from c in seg.Coordinates select grid.CoordinateToPoint(c)).ToArray());
                                }
                                else
                                {
                                    graphics.DrawLines(pen, (from c in seg.Coordinates select grid.CoordinateToPoint(c)).ToArray());
                                }
                            }
                        }
                    }
                    foreach (var player in from player in grid.Players orderby player.Size descending, player.Shape.Size descending select player)
                    {
                        foreach (var pm in player.Positions)
                        {
                            player.Draw(graphics, grid, pm.Position, !pm.IsInvisible, pm.IsInactive, pm.Moves);
                        }
                    }
                    foreach (var effectType in grid.EffectTypes)
                    {
                        var totalPath = new GraphicsPath();
                        var region = new Region();
                        if (effectType.IsInverted)
                        {
                            region.MakeInfinite();
                        }
                        else
                        {
                            region.MakeEmpty();
                        }
                        foreach (var instance in effectType.Instances)
                        {
                            var path = new GraphicsPath();
                            path.AddPolygon((from c in instance.Points select grid.CoordinateToPoint(c)).ToArray());
                            totalPath.AddPath(path, false);
                            if (effectType.IsInverted)
                            {
                                region.Exclude(path);
                            }
                            else
                            {
                                region.Union(path);
                            }
                        }
                        if (effectType.Opacity > 0)
                        {
                            using (var brush = new SolidBrush(effectType.ColorWithOpacity))
                            {
                                graphics.FillRegion(brush, region);
                            }
                        }
                        if (effectType.PerimeterThickness > 0)
                        {
                            using (var pen = new Pen(effectType.PerimeterColor, effectType.PerimeterThickness))
                            {
                                pen.DashStyle = effectType.PerimeterDashStyle;
                                foreach (var instance in effectType.Instances)
                                {
                                    graphics.DrawPath(pen, totalPath);
                                }
                            }
                        }
                    }
                }
                using (var memory = new MemoryStream())
                {
                    image.Save(memory, ImageFormat.Png);
                    Response.BinaryWrite(memory.ToArray());
                    Response.Flush();
                    Response.End();
                }
            }
        }
    }
}